package com.uxsino.simo.query;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.uxsino.commons.logicSelector.PropProxy;
import com.uxsino.commons.utils.config.ConfigProp;
import com.uxsino.commons.utils.debug.SourceInfo;
import com.uxsino.reactorq.model.INDICATOR_TYPE;
import com.uxsino.simo.indicator.*;
import com.uxsino.simo.indicator.retractor.ICompoundValueRetractor;
import com.uxsino.simo.indicator.retractor.IValueRetractor;
import com.uxsino.simo.indicator.retractor.NoneRetractor;
import com.uxsino.simo.networkentity.EntityInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * query template
 * 
 *
 */
public class QueryTemplate implements IQueryGroup {

    private Logger logger = LoggerFactory.getLogger(QueryTemplate.class);

    private int id;

    public boolean isCustom = false;

    @ConfigProp(name = "cmd_pattern", subElement = true, required = true)
    @JsonProperty("cmd_pattern")
    @JSONField(name = "cmd_pattern")
    public String cmdPattern;

    @ConfigProp(name = "priority")
    @JsonProperty("priority")
    @JSONField(name = "priority")
    private int priority = DEFAULT_PRIORITY;

    // private ISelector<EntityInfo> entitySelector;

    @JsonIgnore
    private Map<Indicator, IValueRetractor> retractors = new HashMap<>();;

    private Map<String, String> replaces;

    private Map<String, QueryParameter> parameters = new HashMap<>();

    private String protocolName;

    public Map<String, String> properties = new HashMap<>();;

    public QueryTemplate(int id) {
        this.id = id;
    }

    @Override
    public boolean retractsIndicator(Indicator indicator, String fieldName) {
        IValueRetractor retractor = retractors.get(indicator);
        if (null == retractor) {
            return false;
        }
        if (fieldName == null) {
            return true;
        }
        if (!(retractor instanceof ICompoundValueRetractor)) {
            logger.error("retractor {} is not a compound value retractor, no field will be retracted",
                retractor.getClass().getSimpleName());
            return false;
        }

        return ((ICompoundValueRetractor) retractor).retractsField(fieldName);
    }

    public void addRetractor(Indicator ind, IValueRetractor retractor) {
        if (ind instanceof IExprItem) {

            return;
        }

        retractors.put(ind, retractor);
    }

    public void addReplaces(Map<String, String> r) {

        this.replaces = r;
    }

    public Object replaceChar(Object result) {

        if (result == null || replaces == null || replaces.isEmpty()) {
            return result;
        } else {
            for (Map.Entry<String, String> entry : replaces.entrySet()) {
                result = result.toString().replaceAll(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    @JsonIgnore
    @JSONField(serialize = false)
    public Map<Indicator, IValueRetractor> getRetractors() {
        return retractors;
    }

    @JsonProperty("retracts")
    @JSONField(name = "retracts")
    public Map<String, IValueRetractor> getRetractorsWithIndicatorName() {
        return retractors.entrySet().stream()
            .collect(Collectors.toMap(entry -> entry.getKey().name, Map.Entry::getValue));
    }

    // public ISelector<EntityInfo> getNESelector() {
    // return entitySelector;
    // }

    @JsonIgnore
    @JSONField(serialize = false)
    public String getProtocolName() {
        return protocolName;
    }

    public void setProtocolName(String protocolName) {
        this.protocolName = protocolName;
    }

    // public void setNESelector(ISelector<EntityInfo> selector) {
    // this.entitySelector = selector;
    // }

    /**
     * This extracts the indicator value from the parameter {@code buf}, associate it to the {@code neInfo}
     * and put it into the {@link NSIndicatorDepo} associated to the {@code ctxt}.
     * <br />
     * It also adds the {@link NEIndicatorDepo} corresponding to {@code neInfo} to the {@link NSIndicatorDepo}
     * if it is not already present.
     * @param ctxt
     * @param entity
     * @param buf
     */
    public void retractIndicators(EntityInfo entity, QueryContext ctxt, Object buf, long costMs) {
        for (Map.Entry<Indicator, IValueRetractor> entry : retractors.entrySet()) {
            Indicator ind = entry.getKey();
            if (ind.getIndicatorType() != INDICATOR_TYPE.NONE && !(entry.getValue() instanceof NoneRetractor)) {
                IValueRetractor retractor = entry.getValue();
                Object r = retractor.retract(entity, ctxt, this, buf);
//                retractor.postRetract(entity, ctxt, this, r);
//                r = retractor.cast(r);//类型转换，数据筛选。
                ind.doPostQuery(entity, ctxt, r);
                ctxt.setIndicatorValue(entity, ind, getId(), r, costMs);
            }
        }
    }

    @JsonAnyGetter
    private Map<String, String> getProperties() {
        return properties;
    }

    @PropProxy
    public String getProperty(String propName) {
        return properties.get(propName);
    }

    @JsonAnySetter
    @ConfigProp(name = "*")
    public void setProperty(String propName, String value) {
        properties.put(propName, value);
    }

    @Override
    public void foreach(Consumer<QueryTemplate> action) {
        action.accept(this);
    }

    @Override
    public int count() {
        return 1;
    }

    @Override
    public String getQueryType() {
        return QUERY_TYPE_QUERY;
    }

    public void addParameter(String name, QueryParameter param) {
        parameters.put(name, param);
    }

    @JsonInclude(Include.NON_EMPTY)
    public Map<String, QueryParameter> getParameters() {
        return parameters;
    }

    public QueryParameter getParameter(String name) {
        return parameters.get(name);
    }

    public boolean isParameterized() {
        return parameters.size() > 0;
    }

    public int getId() {
        return id;
    }

    @Override
    public void forEachIndicator(Consumer<Indicator> action) {
        for (Indicator ind : retractors.keySet()) {
            action.accept(ind);
        }
    }

    @Override
    public int getPriority() {
        return priority;
    }

    private SourceInfo sourceInfo = new SourceInfo();

    @Override
    public SourceInfo getSourceInfo() {
        return sourceInfo;
    }

    @Override
    @JsonIgnore
    public void setSourceInfo(String sourceName, String sourceLocation) {
        sourceInfo.source = sourceName;
        sourceInfo.location = sourceLocation;
    }

    @Override
    public boolean isCustom() {
        return isCustom;
    }
}
